﻿using ICSharpCode.SharpZipLib.Core;
using ICSharpCode.SharpZipLib.Zip;
using RESTFinder.Service.Models;
using RESTFinder.Service.Repositories;
using RESTFinder.Service.Utils;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace RESTFinder.Service.Domains
{
    public class DocumentDomain : IDocumentDomain
    {
        private IDocumentRepository _documentRepository;
        private Repositories.IAppRepository _appRepository;
        private string rootPath;

        public DocumentDomain(IDocumentRepository documentRepository, IAppRepository appRepository)
        {
            _documentRepository = documentRepository;
            _appRepository = appRepository;
            rootPath = _appRepository.AppConfigModel.RootPath;
            //DirectoryScan(_appRepository.AppConfigModel.RootPath);
        }

        private void DirectoryScan(string rootPath)
        {
            if(!string.IsNullOrEmpty(rootPath)&& !Directory.Exists(rootPath))
            {
                Directory.CreateDirectory(rootPath);
            }

            var storeLastWriteTime = _documentRepository.GetLastWriteTime();

            var lastWriteTime = Directory.GetLastWriteTime(rootPath);

            if(lastWriteTime != storeLastWriteTime)
            {
                //开始扫描
                //var entries = QueryEngine.Engine.GetAllFilesAndDirectories(rootPath);
            }
        }

        public string Push(string name,string extension, string contentType, Stream stream,string qyCode)
        {
            int  start =  0;
            int  end =  10 * 1024 * 1024;
            if(end > stream.Length)
            {
                end = (int)stream.Length;
            }

            var buffer = new byte[end];

            int c = stream.Read(buffer, 0, buffer.Length);

            var md5 = MD5Util.GetMd5(buffer);

            var realDate = DateTime.Now;

            string filePath;
            if (string.IsNullOrEmpty(qyCode))
            {
                filePath = string.Format("{0}\\{1:yyyyMM}\\{2}{3}", rootPath, realDate, md5, extension);
            }
            else
            {
                filePath = string.Format("{0}\\{4}\\{1:yyyyMM}\\{2}{3}", rootPath, realDate, md5, extension,qyCode);
            }

            if (!_documentRepository.Exist(md5) && SaveDisk(md5, filePath, stream))
            {
                _documentRepository.SaveDocument(md5, name, extension, contentType, filePath, realDate);
            }
            return md5;
        }

        public string PushChunk(string wholeMd5,int chunk,string name, string extension, string contentType, Stream stream)
        {
            var buffer = new byte[stream.Length];

            int c = stream.Read(buffer, 0, buffer.Length);
            var md5 = MD5Util.GetMd5(buffer);
            var realDate = DateTime.Now;
            var filePath = string.Format("{0}\\{1}\\{2:D3}.{3}", rootPath, wholeMd5, chunk, md5);
            SaveDisk(md5, filePath, stream);
            return md5;
        }

        public System.Dynamic.ExpandoObject FileMerge(string fileMd5,string name,string contentType, string ext)
        {
            dynamic re = new System.Dynamic.ExpandoObject();
            try
            {
                var chunkPath = string.Format("{0}\\{1}", rootPath, fileMd5);
                var outputFile = string.Format("{0}\\{1}.{2}", rootPath, fileMd5, ext);
                if (Directory.Exists(chunkPath))
                {
                    var chunkFiles = Directory.GetFiles(chunkPath);

                    var realDate = DateTime.Now;
                    var filePath = string.Format("{0}\\{1:yyyyMM}\\{2}{3}", rootPath, realDate, fileMd5, ext);

                    if (!_documentRepository.Exist(fileMd5)&& SaveDisk(filePath,chunkFiles))
                    {
                        _documentRepository.SaveDocument(fileMd5, name, ext, contentType, filePath, realDate);
                    }

                    Directory.Delete(chunkPath, true);
                }
                re.result = true;
            }
            catch (Exception)
            {

                throw;
            }
            return re;
        }

        private bool SaveDisk(string md5, string filePath, Stream stream)
        {
            var dir = Path.GetDirectoryName(filePath);
            if (!Directory.Exists(dir))
            {
                Directory.CreateDirectory(dir);
            }

            using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write))
            {
                stream.Seek(0, SeekOrigin.Begin);
                stream.CopyTo(fileStream);
                fileStream.Close();
            }

            return true;
        }

        private bool SaveDisk(string filePath,string[] chunkFiles )
        {
            var dir = Path.GetDirectoryName(filePath);
            if (!Directory.Exists(dir))
            {
                Directory.CreateDirectory(dir);
            }
            using (Stream stream = new FileStream(filePath, FileMode.Create, FileAccess.Write))
            {
                foreach (string chunkFile in chunkFiles)
                {
                    using (Stream input = File.OpenRead(chunkFile))
                    {
                        input.CopyTo(stream);
                    }
                }
            }

            return true;
        }

        private byte[] GetFile(string fullPath)
        {
            return File.ReadAllBytes(fullPath);
        }


        public  dynamic   Fetch(string md5)
        {
            var doc = _documentRepository.Fetch(md5);

            if (doc == null) return null;

            return new
            {
                md5 = doc.Md5,
                contenttype = doc.ContentType,
                name = Path.GetFileNameWithoutExtension(doc.FileName),
                extension = doc.Extension.TrimStart('.'),
                rdate = doc.RDate,
                file = doc.FullPath,
            };
        }

        public IEnumerable<dynamic> Fetch(string[] md5s)
        {
            var docs = _documentRepository.Fetch(md5s);

            return docs.Select(o => new
            {
                md5 = o.Md5,
                contenttype = o.ContentType,
                name = Path.GetFileNameWithoutExtension(o.FileName),
                extension = o.Extension.TrimStart('.'),
                rdate = o.RDate,
                file = o.FullPath,
            });
        }

        public System.Dynamic.ExpandoObject Exist(string md5)
        {
            var result = _documentRepository.Exist(md5);

            dynamic re = new System.Dynamic.ExpandoObject();
            re.result = result;
            if (!result)
            {
                //不存在，寻找分片内容。
                string chunkPath = Path.Combine(_appRepository.AppConfigModel.RootPath, md5);

                if(Directory.Exists(chunkPath))
                {
                    re.chunkMd5s = Directory.GetFiles(chunkPath).Select(o =>
                    {
                        return Path.GetFileName((string)o).Split('.')[1];
                    }).ToList();
                }
            }

            return re;
        }

        public string Compress(IEnumerable<dynamic> documents)
        {
            var temp = Path.Combine(_appRepository.AppConfigModel.RootPath, "ZipTemp");
            if(!Directory.Exists(temp))
            {
                Directory.CreateDirectory(temp);
            }

            var md5s = string.Join(",", documents.Select(o => o.md5));

            var md5 = MD5Util.GetMd5(md5s);

            var zipName = string.Format("{0}\\{1}{2}", temp, md5, ".zip");

            if (File.Exists(zipName))
                return zipName;

            using (ZipOutputStream zipOutputStream = new ZipOutputStream(File.Create(zipName)))
            {
                zipOutputStream.SetLevel(9);
                var abyBuffer = new byte[4096];

                foreach (var document in documents)
                {
                    string filename = document.file;
                    string name = document.name;
                    string extension = document.extension;
                    using (FileStream filestream = File.OpenRead(filename))
                    {
                        var zipEntry = new ZipEntry(name + "." + extension);
                        zipEntry.DateTime = DateTime.Now;
                        zipEntry.Size = filestream.Length;

                        zipOutputStream.PutNextEntry(zipEntry);
                        StreamUtils.Copy(filestream, zipOutputStream, abyBuffer);
                    }
                }
            }

            return zipName;
        }

        public string CompressNestedFolder(IEnumerable<dynamic> documents, IEnumerable<Tuple<string, string>> zipEntryItems)
        {
            var temp = Path.Combine(_appRepository.AppConfigModel.RootPath, "ZipTemp");
            if (!Directory.Exists(temp))
            {
                Directory.CreateDirectory(temp);
            }

            var md5s = string.Join(",", zipEntryItems.Select(o=>o.Item1));

            var md5 = MD5Util.GetMd5(md5s);

            var zipName = string.Format("{0}\\{1}{2}", temp, md5, ".zip");

            if (File.Exists(zipName))
                return md5;

            using (ZipOutputStream zipOutputStream = new ZipOutputStream(File.Create(zipName)))
            {
                zipOutputStream.SetLevel(9);
                var abyBuffer = new byte[4096];

                foreach (var zipEntryItem in zipEntryItems)
                {
                    var document = documents.FirstOrDefault(o => o.md5 == zipEntryItem.Item1);
                    if (document == null) continue;
                    string filename = document.file;
                    string name = document.name;
                    string extension = document.extension;
                    using (FileStream filestream = File.OpenRead(filename))
                    {
                        var zipEntry = new ZipEntry(zipEntryItem.Item2);
                        zipEntry.DateTime = DateTime.Now;
                        zipEntry.Size = filestream.Length;

                        zipOutputStream.PutNextEntry(zipEntry);
                        StreamUtils.Copy(filestream, zipOutputStream, abyBuffer);
                    }
                }
            }

            return md5;
        }

        public string GetZipFile(string md5)
        {
            var temp = Path.Combine(_appRepository.AppConfigModel.RootPath, "ZipTemp");
            var zipName = string.Format("{0}\\{1}{2}", temp, md5, ".zip");

            if (File.Exists(zipName))
                return zipName;
            return "";
        }
    }
}
